
#ifndef _C_CLUSTERING_H_
#define _C_CLUSTERING_H_





#include "sl_.h"


/// <summary>
/// k均值聚类
/// </summary>
typedef struct __k_means___
{
    int* sample_model_1;       //数组长度：N。标记每个样本所属的模型中心。     
    int* sample_model_2;       //数组长度：N。标记每个样本所属的模型中心。 这两个是有实体的，用于迭代。 

    int K;   //K个中心
    int N;   //N个数据
    int D;   //D维数据

    int* means_count;        //数组长度：K。标记每个聚类中心的样本数。
    int* sample_model;       // 指向私有变量 sample_model_1或者sample_model_2 ,数组长度：N。标记每个样本所属的模型中心。   
    vec* sample_vector;  //N个D维矢量，类内单独分配的内存。样本数据用矢量形式表示比较方便运算。样本不允许有相同的矢量存在。
    vec* mean_vector;   //K个D维矢量。聚类中心矢量
}K_Means;

/// <summary>
/// 根据K，N，D分配内存。
/// </summary>
void K_Means_allocate(K_Means* kmeans);

/// <summary>
/// 释放内存
/// </summary>
void K_Means_deallocate(K_Means* kmeans);

/// <summary>
/// clustering 聚类。
/// </summary>
/// <param name="k">聚类中心个数</param>
/// <param name="sample">样本矩阵，每个行矢量都是一个样本</param>
/// <param name="max_iterate_count">最大的迭代次数。迭代次数不会超过它</param>
/// <returns>迭代次数</returns>
int K_Means_clustering(K_Means* kmeans, int k, mat sample, int max_iterate_count);
//

    /// <summary>
    /// 测试k均值分布。 用达到的类：Vector，Gauss，Matrix，TwoFunctionPicture。
    /// </summary>

void test_k_means(void);
//




//高斯混合模型 Gauss Mixture Model
typedef struct __gmm____
{
    /*
    高斯混合模型迭代算法：
    1、初始化k个模型的均值，协方差， 混合系数pi。 由k均值聚类的结果进行初始化。然后进入2，3步的迭代。

    2、计算样本在各个模型的比重，即gamma矩阵。gamma[n][k]表示第n个样本是第k个模型生成的概率
        = pi[k]*gauss[k].probability(sample_n)/ sum_ki( pi[ki]*gauss[ki].probability(sample_n) )

    3、利用当前gamma矩阵重新估计高斯分布，及分布比重。
        Nk = sum_n gamma[n][k]
        gauss[k].average = Nk * sum_n ( gamma[n][k] * sample_n)
        gauss[k].cov =  Nk * sum_n ( gamma[n][k] * (sample_n - gauss[k].average)* (sample_n - gauss[k].average)^T )
        pi[k] = Nk/N

    4、如果参数收敛了，就可以停止迭代了。这里用gamma矩阵来判断收敛。gamma所有元素相加和为N。
        如果gamma所有元素的变化相差小于N*0.01，即平均每个样本在k个模型中的概率变化小于1%，则停止迭代。
    */

    mat* gamma;          //样本的概率，指向gamma_1或gamma_2 。 row*col = N*K。  gamma.data[n][k]表示低n个样本属于第k个模型的概率。每行的和为1.
    mat gamma_1;         //样本的概率。 row*col = N*K。  gamma.data[n][k]表示低n个样本属于第k个模型的概率。每行的和为1.
    mat gamma_2;         //样本的概率。 row*col = N*K。  gamma.data[n][k]表示低n个样本属于第k个模型的概率。每行的和为1.

    int K;   //K个模型
    int N;   //N个数据
    int D;   //D维数据

    gauss_vec* gauss;  //数组长度：K。高斯分布的数组。 可以用K均值聚类的协方差和中心来初始化。
    real_t* pi;        //数组长度：K。每个模型占据的比重。累加=1。

    vec* sample_vector;  //N个D维矢量，类内单独分配的内存。样本数据用矢量形式表示比较方便运算。
    int* sample_model;   //数组长度：N。标记每个样本所属的模型。sample_model[n]的值是gamma第n行最大的元素。收敛后才计算。

}GMM;


/// <summary>
/// 根据K，N，D分配内存。
/// </summary>
void GMM_allocate(GMM* gmm);
/// <summary>
/// 释放内存
/// </summary>
void GMM_deallocate(GMM* gmm);


/// <summary>
/// 第一次初始化参数。使用K均值聚类初始化。
/// </summary>
/// <param name="sample">样本数据</param>
void GMM_init_parameter(GMM* gmm, mat sample, mat m, vec v);

/// <summary>
/// E步骤：重新估计责任gamma
/// 即重新估计分布
/// </summary>
void GMM_Estep(GMM* gmm);

/// <summary>
/// M步骤：重新估计参数
/// 即重新估计高斯分布的参数
/// mat 是D*D大小的矩阵， v 的尺寸也是 D。用于辅助计算，保存临时变量。避免迭代的时候频繁分配内存。
/// </summary>
void GMM_Mstep(GMM* gmm, mat m, vec v);

/// <summary>
/// converge 收敛, 汇聚, 衔接, 会聚, 汇合, 汇集。
/// 这里用gamma矩阵来判断收敛。gamma所有元素相加和为N。
/// 如果gamma所有元素的变化相差小于N * 0.01，即平均每个样本在k个模型中的概率变化小于1 % ，则停止迭代。
/// </summary>
bool GMM_is_converge(GMM* gmm);



/// <summary>
/// 输入数据，计算高斯混合模型
/// </summary>
/// <param name="k">模型数量</param>
/// <param name="sample">样本数据</param>
/// <param name="max_iterate_count">最大的迭代次数。迭代次数不会超过它</param>
/// <returns>迭代次数</returns>
int GMM_calculate(GMM* gmm, int k, mat sample, int max_iterate_count);


void test_gmm(void);
//


void test_k_means(void);






#endif




